<?php

/**
 * Third Account class for Users.
 */
class PCompanyModelThirdAccounts extends JModelLegacy
{
   /**
    * get company thirdtools account info
    */
   public function getCompanyThirdToolsInfo() {
      $db = PFactory::getDbo();
      $query = "SELECT * FROM *PREFIX*hr_thirdtools_login WHERE uid=" . $db->quote(OC_User::getUser()); 
      $db->setQuery(PUtil::replaceOCPrefix($query));
      return $db->loadAssoc();
   }
   
   public function saveSyncAccount(){
      // simple spoof check security
      if(!PSession::checkToken()) {
         $this->setError('登录已失效，请重新登录');
         return false;
      }

      $username = JRequest::getString('account_username');
      $type = JRequest::getString('type');
      $password = JRequest::getString('account_password');
      $rememberme = JRequest::getInt('rememberme', 0);
      $membername = JRequest::getString('account_membername');

      if(empty($username) || empty($type)) {
         $this->setError('请填写您的账户信息');
         return false;
      }
      
      $result = $this->getCompanyThirdToolsInfo();
      $account_info = $result['login'] ? json_decode($result['login'],true) : array();
      
      if(empty($account_info[$type]))
         $account_info[$type] = array(); 

      $account_info[$type]['username'] = $username;

      // if it's just *, ignore it 
      if(!empty($password) && strlen(str_replace('*', '', $password)) > 0){
         //FIXME: need to encrypt 
         $account_info[$type]['password'] = $rememberme ? $password : '';
         $account_info[$type]['passwordlen'] = strlen($password); 
         $account_info[$type]['password'] = PCompanyHelper::encrypt($account_info[$type]['password']);
      }

      // keep it for reference for backend 
      $account_info[$type]['rememberme'] = $rememberme;
      if(!$rememberme) {
         $account_info[$type]['password'] = ''; 
         $account_info[$type]['passwordlen'] = 0;
      }

      if($membername){
         $account_info[$type]['membername'] = $membername;
      }
      
      $user = PFactory::getMasterUser();
      $db = PFactory::getDbo();
      $query = "INSERT INTO *PREFIX*hr_thirdtools_login (uid, login) VALUES (" . 
               $db->quote(OC_User::getUser()) . ", " . 
               $db->quote(json_encode($account_info)) . 
               ") " . 
               " ON DUPLICATE KEY UPDATE login=values(login)"; 
      
      $db->setQuery(PUtil::replaceOCPrefix($query));
      if(!$db->execute()) {
         return false;
      }
      return true;
   }

   private function getSyncSummary($type, $actions, $param=array()) {
      $summary = '';
      switch($type) {
         case '51job': 
         default: 
            $summary = '前程无忧'; 
         break; 
         case 'zhilian': 
            $summary = '智联招聘'; 
         break;
         case 'jiancai':
            $summary = '建筑英才';
         break;
         case 'lagou':
            $summary = '拉勾';
         break;
         // others 
      }

      if(!$param) $param = array();
      $values = array();
      // format: array('jobids'=>array(1, 2, 0));
      if(count($param) > 0 && array_key_exists('jobids', $param)) {
         $values = array_values($param);
         $values = array_shift($values);
         $values = array_filter(array_values($values));
      }

      if((array_key_exists('jobids', $param) || array_key_exists('jobid', $param)) && count($values) > 0) {
         switch (array_shift($actions)){
            case 'renewjobs':
               $summary .= count($values).'个职位刷新';
            break;
            default:
               $summary = count($values) . "个职位推送到" . $summary;
            break;
         }
      }
      else {
         // we only look at first action if actions are chained together 
         switch(array_shift($actions)) {
            case 'fetchresumes':
               $summary .= '简历导入';
            break;
            case 'fetchcompany':
               $summary .= '公司信息下载';
            break;
            case 'searchresumes':
               $summary .= '人才搜索';
            break;
            case 'saveresumes':
               $summary .= '简历导入';
            break;
            case 'fetchjobs':
            default:
               $summary .= '数据下载';
            break;
            // other things in future
         }
      }

      return $summary;
   }

   private function setHidden($type, $actions) {
      
      // if an action doesn't have to be shown, make it hidden 
      switch(array_shift($actions)) {
         case 'searchresumes':
            return 1;
         break; 
      }

      return 0; 
   }

   /**
    * @brief try login sync. if session is okay. task is added 
    * @param type - type of account: 51job or zhilian 
    * @param actions - list of actions: fetchjobs addjob fetchseekers fetchcompany (whatever we support). Please note login will be performed by backend automatically
    * @param param - input parameter in array key-value pair for action
    * @param captcha - if login needs captcha, it will be filled in with captcha image base64 string 
    * @return mixed - object for good, false for error or something else 
    */
   public function trySync($type, $actions=array(), $param=array(), &$captcha='') {
      $sync_info = $this->getCompanyThirdToolsInfo();
      if(!$sync_info)
         $sync_info = array();

      $thirdaccounts_info = !empty($sync_info['login']) ? json_decode($sync_info['login'],true) : null;
      $account_cookie = !empty($sync_info['cookie']) ? json_decode($sync_info['cookie'],true) : null;


      $thirdaccount = empty($thirdaccounts_info[$type]) ? null : $thirdaccounts_info[$type];
      $cookie = empty($account_cookie[$type]) ? null : $account_cookie[$type];

      $username = isset($thirdaccount['username']) ? $thirdaccount['username'] : '';
      $membername = isset($thirdaccount['membername']) ? $thirdaccount['membername'] : '';

      // zhilian is enforced to get captcha, even if the cookie doesn't exist 
      if($cookie || $type == 'zhilian'){
         // we have cookie, try to login to see if we are still valid
         $command = "/usr/bin/node " . realpath(JPATH_ROOT."/../cloud/daemon/thirdtools/synctool.js");
         try {
            // very common, http connection will time out in 60 seconds 
            $output = execTimeout($command . " --uid " . OC_User::getUser() . " --type " . $type . " --action login --dataaction " . implode(",", $actions), 45);
         }
         catch(Exception $e) {
            $this->setError("系统出现错误，请报告客服！: " . $e->getMessage());
            return false; 
         }

         // when the command returns back, we have the data in db 
         $sync_info = $this->getCompanyThirdToolsInfo();
         if(!$sync_info)
            $sync_info = array();

         $status = !empty($sync_info['status']) ? json_decode($sync_info['status'], true) : null;
         $status = empty($status[$type]) ? null : $status[$type];

         $hidden = $this->setHidden($type, $actions); 

         if($status['result']) {
            // check if the actions is only login
            if($status['result'] == 'success' && count($actions) ==1 && $actions[0] == 'login') {
               $this->setError("trylogin-success");
               return false; 
            } 
            elseif($status['result'] == 'success') {
               // out cookie are solid, move the task into background 
               // add to delay task 
               // the parameters has extra membername and username to uniquely identify a ask 
               if($actions[0] == "autofetchjobs" || $actions[0] == "autorenewjobs"){
                  if(!$this->addAutoAction($actions[0],$type))
                     return false;
               }else{
                  $taskactions = array_diff($actions, array("autofetchjobs", "autorenewjobs"));
                  if(count($taskactions) <= 0) {
                     // only autofetchjobs or autorenewjobs
                     // enable auto fetch/renew in oc_hr_thirdparty_login/oc_hr_thirdparty_auto tables
                     return;
                  }
                  // when we reach here, it's safe to create delay tasks
                  $actions = $taskactions;
                  DelayTaskJob::add(
                     array('summary'=>$this->getSyncSummary($type, $actions, $param), 
                        'userid'=>PFactory::getMasterUser()->get('id'), 
                        'owner'=>OC_User::getUser(),
                        'handler'=>$command,
                        'parameters'=>json_encode(array('type'=>$type, 'membername'=>$membername, 'username'=>$username)),
                        'dataparam'=>json_encode(array('actions'=>$actions, 'param'=>$param, 'demomode'=>(isDemoUser() ? 1:0))),
                        'exclusive'=> ($type == '51job' ? true : false),  // support multiple sync concurrently 
                        'unique'=>false,  // only one same task in the queue waiting 
                        'priority'=>9, // put it as highest priority 
                        'hidden'=>$hidden
                       ),
                     $taskid
                  );
                  PResumesHelper::updateTermTaskStatus($taskid,$type,$actions,$param);
               }
               $this->setError($hidden ? "hidden-task-add-to-background" : "task-add-to-background");
               return false; 
            }
            else if($status['result'] == 'captcha') {
               // output has captcha 
               $output = preg_split('/\n+/', $output);
               foreach($output as $line) {
                  $line = trim($line);
                  if(strlen($line) <= 0) continue; 
                  if(strpos($line, 'logincaptcha:') === 0) {
                     $captcha = preg_replace("/^logincaptcha:/", '', $line);
                     break;
                  }
               }
            }
            else {
               // cookie error, need to re-login, pop up the window so user has chance to enter new account information if he wants 
               // fall through 
            }
         }

      }

      return $sync_info;
   }

   /**
    * @brief start sync. This will be called when try sync fails at first place (login failure or captcha needed)
    * @param type - type of account: 51job or zhilian 
    * @param actions - list of actions: fetchjobs addjob fetchseekers fetchcompany (whatever we support). Please note login will be performed by backend automatically
    * @param login - account login information 
    * @param param - input parameter in array key-value pair for action
    * @param captcha - if login needs captcha, it will be filled in with captcha image base64 string 
    * @return mixed - object for good, false for error or something else 
    */
   public function startSync($type, $actions=array(), $login=array(), $param=array(), &$captcha='') {
      // $login should have complete account login information if we reach here 
      if(empty($login['username'])) {
         $this->setError('请填写用户名');
         return false; 
      }
      if(empty($login['password'])) {
         $this->setError('请填写密码');
         return false; 
      }

      switch($type) {
         case '51job': 
            if(empty($login['membername'])) {
               $this->setError('请填写会员名');
               return false; 
            }
            if ($login['captcha'] != null && empty($login['captcha'])) {
               $this->setError('请填写验证码');
               return false;
            }
         break; 
         case 'zhilian': 
            if(empty($login['captcha'])) {
               $this->setError('请填写验证码');
               return false; 
            }
         break; 
         case 'jiancai': 
            //if(empty($login['captcha'])) {
            //   $this->setError('请填写验证码');
            //   return false; 
            //}
         break; 
      }

      $command = "/usr/bin/node " . realpath(JPATH_ROOT."/../cloud/daemon/thirdtools/synctool.js");

      $sync_info = $this->getCompanyThirdToolsInfo();
      $status = !empty($sync_info['status']) ? json_decode($sync_info['status'], true) : null;
      $status = empty($status[$type]) ? null : $status[$type];

      $hidden = $this->setHidden($type, $actions); 

      // for 51job, we need to check if there any existing exclusive login (same membername and username) running 
      if(!empty($status['result']) && $status['result'] == 'success' && empty($login['captcha']) && DelayTaskJob::checkRunningExclusive(array('handler' => $command,
                                                   'parameters' => json_encode(array('type'=>$type, 'membername'=>$login['membername'], 'username'=>$login['username'])),
                                                   'owner'=>OC_User::getUser()))) {

         // we have to add it into queue for later processing. 
         // Unfortunately we cannot verify if the username/password/anything is correct at this stage 
         // FIXME: how to improve

         if($actions[0] == "autofetchjobs" || $actions[0] == "autorenewjobs"){
            if(!$this->addAutoAction($actions[0],$type))
               return false;
         }else{
            $taskactions = array_diff($actions, array("autofetchjobs", "autorenewjobs"));
            if(count($taskactions) <= 0) {
               // only autofetchjobs or autorenewjobs
               // enable auto fetch/renew in oc_hr_thirdparty_login/oc_hr_thirdparty_auto tables
               return;
            }
            // when we reach here, it's safe to create delay tasks
            $actions = $taskactions;
            for($i=0; $i<count($actions); $i++) {
               DelayTaskJob::add(
                  array('summary'=>$this->getSyncSummary($type, array($actions[$i]), $param),
                  'userid'=>PFactory::getMasterUser()->get('id'),
                  'owner'=>OC_User::getUser(),
                  'handler'=>$command,
                  'parameters'=>json_encode(array('type'=>$type, 'membername'=>$login['membername'], 'username'=>$login['username'])),
                  'dataparam'=>json_encode(array('actions'=>array($actions[$i]), 'param'=>$param, 'demomode'=>(isDemoUser() ? 1:0))),
                  'exclusive'=> ($type == '51job' ? true : false),  // support multiple sync concurrently
                  'unique'=>false,  // only one same task in the queue waiting
                  'priority'=>9, // put it as highest priority
                  'hidden'=>$hidden
                  ),
                  $taskid
               );
               PResumesHelper::updateTermTaskStatus($taskid,$type,array($actions[$i]),$param);
            }
         }
         return $hidden ? "hidden-task-add-to-background" : "task-add-to-background";
      }

      try {
         $argv = " --uid " . OC_User::getUser() . " --type " . $type . " --action login --dataaction " . implode(",", $actions);
         $argv .= ' --rememberme ' . escapeshellarg($login['rememberme']) . ' --username ' . escapeshellarg($login['username']) . ' --password ' . escapeshellarg($login['password']);
         switch($type) {
            case '51job': 
               $argv .= ' --membername ' . escapeshellarg($login['membername']).' --captcha ' . escapeshellarg($login['captcha']);
            break; 
            case 'zhilian': 
               $argv .= ' --captcha ' . escapeshellarg($login['captcha']); 
            break; 
         }

         // try to login now
         $output = execTimeout($command . $argv, 45);
      }
      catch(Exception $e) {
         $this->setError("系统出现错误，请报告客服！: " . $e);
         return false; 
      }

      // when the command returns back, we have the data in db 
      $sync_info = $this->getCompanyThirdToolsInfo();
      if(!$sync_info)
         $sync_info = array();

      $status = !empty($sync_info['status']) ? json_decode($sync_info['status'], true) : null;
      $status = empty($status[$type]) ? null : $status[$type];

      if($status['result']) {
         if($status['result'] == 'success' && count($actions) ==1 && $actions[0] == 'login') {
            return "login-success"; 
         } 
         elseif($status['result'] == 'success') {
            // out cookie is solid, move the task into background 
            // add to delay task 
            // the parameters has extra membername and username to uniquely identify a ask 
            if($actions[0] == "autofetchjobs" || $actions[0] == "autorenewjobs"){
               if(!$this->addAutoAction($actions[0],$type))
                  return false;
            }else{
               $taskactions = array_diff($actions, array("autofetchjobs", "autorenewjobs"));
               if(count($taskactions) <= 0) {
                  // only autofetchjobs or autorenewjobs
                  // enable auto fetch/renew in oc_hr_thirdparty_login/oc_hr_thirdparty_auto tables
                  return;
               }
               // when we reach here, it's safe to create delay tasks
               $actions = $taskactions;
               for($i=0; $i<count($actions); $i++) {
                  DelayTaskJob::add(
                  array('summary'=>$this->getSyncSummary($type, array($actions[$i]), $param),
                  'userid'=>PFactory::getMasterUser()->get('id'),
                  'owner'=>OC_User::getUser(),
                  'handler'=>$command,
                  'parameters'=>json_encode(array('type'=>$type, 'membername'=>$login['membername'], 'username'=>$login['username'])),
                  'dataparam'=>json_encode(array('actions'=>array($actions[$i]), 'param'=>$param, 'demomode'=>(isDemoUser() ? 1:0))),
                  'exclusive'=> ($type == '51job' ? true : false),  // support multiple sync concurrently
                  'unique'=>false,  // only one same task in the queue waiting
                  'priority'=>9, // put it as highest priority
                  'hidden'=>$hidden
                  ),
                  $taskid
                  );
                  PResumesHelper::updateTermTaskStatus($taskid,$type,array($actions[$i]),$param);
               }
            }
            return $hidden ? "hidden-task-add-to-background" : "task-add-to-background";
         }
         else if($status['result'] == 'captcha') {
            // output has captcha 
            $output = preg_split('/\n+/', $output);
            foreach($output as $line) {
               $line = trim($line);
               if(strlen($line) <= 0) continue; 
               if(strpos($line, 'logincaptcha:') === 0) {
                  $captcha = preg_replace("/^logincaptcha:/", '', $line);
                  break;
               }
            }

            return "captcha-reload";
         }
         else {
            // the login failed in one way or another 
            switch($type) {
               case '51job': 
                  if(empty($login['captcha']))
                     $this->setError("会员名、用户名或密码不准确");
                  else 
                     $this->setError("会员名、用户名、验证码或密码不准确");
               break; 
               case 'zhilian': 
                  $this->setError("用户名、验证码或密码错误");
               break; 
               default: 
                  $this->setError("登录失败");
               break; 
            }

            return false; 
         }
      }

      return true; 
   }

   //用于在oc_hr_thirdtools_auto里面生成数据
   public function addAutoAction($syncaction,$type){
      if(empty($syncaction) || empty($type))
         return true; 

      $db = PFactory::getDbo();

      if(!$this->UpdateAutoActionStatus($syncaction,$type,1)) {
         $this->setError("网络异常请刷新重试！");
         return false; 
      }

      $query = "INSERT IGNORE INTO *PREFIX*hr_thirdtools_auto (`uid`,`source`,`actions`) VALUES (".$db->quote(OC_User::getUser()).", ".$db->quote($type).", ".$db->quote($syncaction).")";
      $db->setQuery(PUtil::replaceOCPrefix($query));
      if(!$db->execute()) {
         $this->setError("网络异常请刷新重试！");
         return false; 
      }

      return true;
   }

   //用于更新oc_hr_thirdtools_login,autofetchjobs和autorenewjobs的数据
   public function UpdateAutoActionStatus($syncaction,$type,$stat){
      if(empty($syncaction) || empty($type))
         return true; 

      $db = PFactory::getDbo();
      $result = $this->getCompanyThirdToolsInfo();
      if(empty($result))
         return false;

      if($syncaction == "autofetchjobs"){
         $status = !empty($result['status']) ? json_decode($result['status'], true) : null;

         if(!isset($status[$type]['autofetchjobs']))
            $status[$type]['autofetchjobs'] = array();
         $status[$type]['autofetchjobs']['disabled'] = 0;

         if($stat == 0){
            $status[$type]['autofetchjobs']['disabled'] = 1;
            if(!$this->RemoveAutoActions($syncaction,$type))
               return false;
         }
         $status[$type]['autofetchjobs']['result'] = 'syncing';
         $sql = 'status = '.$db->quote(json_encode($status));
      }else{
         $status = !empty($result['status']) ? json_decode($result['status'], true) : null;
         
         if(!isset($status[$type]['autorenewjobs']))
            $status[$type]['autorenewjobs'] = array();
         $status[$type]['autorenewjobs']['disabled'] = 0;
         
         if($stat == 0){
            $status[$type]['autorenewjobs']['disabled'] = 1;
            if(!$this->RemoveAutoActions($syncaction,$type))
               return false;
         }
         $status[$type]['autorenewjobs']['result'] = 'syncing';
         $sql = 'status = '.$db->quote(json_encode($status));
      }

      $query = "UPDATE *PREFIX*hr_thirdtools_login SET ".$sql." WHERE uid = ".$db->quote(OC_User::getUser());
      $db->setQuery(PUtil::replaceOCPrefix($query));
      if(!$db->execute()) {
         return false;
      }

      CacheHelper::triggerRemove(OC_User::getUser(), array('*PREFIX*hr_thirdtools_login'));
      return true;
   }

   public function RemoveAutoActions($syncaction,$type){
      if(empty($syncaction) || empty($type))
         return false;

      $db = PFactory::getDbo();
      $query = "DELETE FROM *PREFIX*hr_thirdtools_auto  WHERE uid = ".$db->quote(OC_User::getUser())." AND source = ".$db->quote($type)." AND actions = ".$db->quote($syncaction);

      $db->setQuery(PUtil::replaceOCPrefix($query));
      if(!$db->execute()) {
         return false;
      }

      return true;
   }
}

